iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
IT Operation

地端機房的三十天維運:開源服務封裝、GPU 節點守護與可稽核的變更管理系列 第 23 篇

Day 23|日誌找不到原因?從 tcpdump 最小權限封包擷取到 WORM 封存的網路排錯實戰

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20261007/201418169Iz5ZDEDAn.png

前言:當日誌遇到「網路黑盒子」

在維運的世界裡,不同層級的觀測工具各有其特性:

  • Day 22 的防火牆日誌 能精確記錄誰在什麼時間、對哪個 Port 連線了幾次,
  • Day 21 的系統日誌(Syslog / Journal) 能忠實反映應用程式當時噴出了什麼錯誤字串。

然而,當系統發生深層網路異常,例如 NFS 掛載無預警卡住數分鐘,節點日誌裡反覆跳出核心的警告:

nfs: server 192.168.100.1 not responding, still trying

這時日誌無法回答底層真相,究竟是 NAS 負載過高來不及回應?交換器晶片丟包?還是節點本機的網路卡根本沒把請求送出呢?

日誌說不出線上實際跑了什麼,但封包有答案

在多數企業地端機房中,抓封包往往停留在「出事時工程師用 root 登入敲 tcpdump -i any -w /tmp/debug.pcap」。這種臨時做法隱藏著三個資安與維運問題:

  1. 特權過大:抓包者必須具備全系統 root 權限,
  2. 敏感資料外洩:預設往往抓取整個 Payload,檔案內容一覽無遺,
  3. 無合規封存機制:產出的檔案隨意散落在 /tmp,伺服器重開即消失,無法作為事後稽核或長期比對的基準(Baseline)。

今天要打破這種隨性習慣,將封包擷取改造成標準化、最小權限(Least Privilege)的 systemd 服務平常預設僅抓標頭(Header)、執行完畢自動封裝校驗,並經由獨立受限的通道匯流至 Day 21 建置的 WORM 不可竄改儲存池。


一、權限設計:僅賦予 CAP_NET_RAW,多一個都不給

在 Day 11 的安全架構中,我們將節點帳號劃分出專用服務帳號,一律禁用 sudo 與 Docker 群組。在此基礎上:

  • Day 12 的 gb10-host-guard 採用專用帳號 svc-guard,僅賦予單一 CAP_KILL,
  • Day 19 的 gb10-textfile.service 僅賦予單一 CAP_SYSLOG,
  • 今天的 pcap@.service 是第三個貫徹此精神的服務:建立專用帳號 pcap,僅賦予單一能力 CAP_NET_RAW。

為什麼不需要 CAP_NET_ADMIN?

執行 tcpdump 通常需要兩項底層能力:

  1. 開啟 AF_PACKET Socket 接收封包:需要 CAP_NET_RAW。
  2. 將網路卡切換為「混雜模式(Promiscuous Mode)」以竊聽同網段非本機封包:需要 CAP_NET_ADMIN。

在我們的情境中,監控標的是本機對 NAS 的 NFS 流量或本機與 PVE 叢集節點的 Corosync 心跳,皆屬於本機端點流量。因此啟動參數全面強制帶入 -p(禁用混雜模式),徹底拿掉 CAP_NET_ADMIN。

我們在收集端利用 setpriv 模擬無特權帳號驗證這項邊界限制:

# 1. 僅帶有 CAP_NET_RAW:成功啟動抓包
$ setpriv --reuid=pcaptest --regid=pcaptest --init-groups \
    --inh-caps=+net_raw --ambient-caps=+net_raw --bounding-set=-all,+net_raw \
    env PCAP_LIVE=/tmp/capt/live IFACE=lo FILTER="udp port 17998" MODE=timed ROTATE_SECONDS=4 KEEP_FILES=1 \
    /usr/local/bin/pcap-run.sh nfs
pcap-run: nfs on lo snaplen 256 mode timed max 64s filter: udp port 17998
tcpdump: listening on lo, link-type EN10MB (Ethernet), snapshot length 256 bytes
Maximum file limit reached: 1
5 packets captured

# 2. 拔除所有能力:立即被核心拒絕
$ setpriv --reuid=pcaptest --regid=pcaptest --init-groups --bounding-set=-all env ... pcap-run.sh nfs
tcpdump: lo: You don't have permission to perform this capture on that device
(socket: Operation not permitted)

沙盒配置與 AppArmor 驗證

實務上,不對 /usr/bin/tcpdump 下 setcap。因為對二進位檔直接打上能力標籤,任何有權限執行該指令的使用者都能抓包,且 package upgrade 後會失效。將能力嚴格綁定在 systemd unit 的行程樹中:

  • AmbientCapabilities=CAP_NET_RAW 與 CapabilityBoundingSet=CAP_NET_RAW
  • NoNewPrivileges=yes
  • ProtectSystem=strict:全域唯讀,ReadWritePaths= 僅開放 /var/lib/pcap/live 與 /var/lib/pcap/done
  • RestrictAddressFamilies=AF_PACKET AF_NETLINK AF_UNIX AF_INET AF_INET6(僅保留抓包、動態路由判定、日誌傳遞與基本 socket 操作)

在節點 spark01 上啟動 pcap@nfs 後檢視行程,狀態完全符合預期:

  • 行程以 pcap 身份執行,
  • 五組 Linux Capability 集合解碼後全部僅有 cap_net_raw,
  • AppArmor 處於 tcpdump (enforce) 狀態。

pcap@nfs 的行程、能力與檔案擁有者(systemctl status 與 /proc/PID/status 的 CapEff)

AppArmor 實地踩坑備忘:

  • Debian 13 (PVE 9) 與 Ubuntu 24.04 (DGX OS 7):預設的 /etc/apparmor.d/usr.bin.tcpdump 已包含 /**.[pP][cC][aA][pP][0-9]* rw, 規則,因此 -C/-W 產生的環狀檔寫入均不會被擋,無需撰寫 local override。
  • DGX OS 上的無害警告:DGX OS 的 libpcap 編譯時加入了 RDMA 支援,啟動時會嘗試讀取 /etc/libibverbs.d/。AppArmor 會阻擋該讀取(DENIED),journal 會出現一行 warning。由於我們抓取的是乙太網路介面,完全不影響網路封包擷取運作,因此維持原則,不為無害警告破壞 AppArmor 防禦面。

維運值班人員的權限收斂(sudoers-pcap)

提供給無 root 權限維運人員的 sudoers-pcap,限制 pcap-ops 群組只能免密碼操作列名的指定服務實例。
請注意:絕對不能在 sudoers 中寫 systemctl start pcap@*。因為 sudoers 的萬用字元 * 會連同空白一併比對,若寫成 pcap@*,使用者輸入 sudo systemctl start pcap@nfs sshd 也會被放行。我們將允許的 profile 整理明確列出,確保安全邊界無漏洞。


二、服務架構:模板化實例與輪替機制

服務的生命週期設計為:

sudo systemctl start pcap@nfs      # 載入 /etc/pcap/nfs.env
journalctl -fu pcap@nfs            # 即時檢視進度
sudo systemctl stop pcap@nfs       # 手動提早停止,亦會觸發正常封存收尾

pcap@.service 為 systemd 模板服務,%i 代表 profile 名稱。腳本 pcap-run.sh 負責組合參數,而 ExecStopPost 觸發的 pcap-seal.sh 則在行程結束後將 live/ 中的封包移至 done/,並在旁邊產生同名 .sha256 sidecar 檔案作為就緒標記。

四組標準 Profile 矩陣

Profile 監控目標 監聽介面 Snaplen 運作模式 上限機制
nfs 模型庫 NAS (TCP 2049) auto (自動路由解析) 256 timed 每 10 分鐘一檔,滿 6 檔(1 小時)自動退出
corosync 叢集心跳 (UDP 5405~5412, TCP 5403) vmbr0 128 ring 20 檔各 50 MB,環狀覆寫,手動停止
syslog 日誌傳輸 (TCP 514, 9428) auto 128 timed 每 5 分鐘一檔,滿 2 檔自動退出
custom 臨時自訂排錯 auto 256 timed 每 5 分鐘一檔,單次排錯

三個關鍵架構決策

1. Snaplen 256 的陷阱,它不等於「只留標頭」。

如果將 Snaplen 設為 256 bytes,可能有人認為這或許剛好放得下 Ethernet + IP + TCP + RPC + NFSv4 Compound 標頭,不會錄入檔案實體內容。

但實際上,Snaplen 限制的是每個接收到的封包切取長度。在 NFS 大檔讀取情境下,NAS 將 1 MiB 的 READ Response 拆成數千個 TCP 巨型封包傳送。扣除 54 bytes 的乙太網路與 TCP 標頭後,每個封包殘留了約 200 bytes 的實體資料。
在測試載入模型的一小時內,NAS 傳來 46,196 個 TCP 段,在擷取檔中殘留了 9.3 MB 的資料碎片。使用 strings 指令檢視,赫然可見 HuggingFace safetensors 的 JSON 檔頭。

防禦決策:
Snaplen 256 是一種權衡(Trade-off),能看見 RPC 呼叫與檔名操作,但絕非資料隔離牆。

  • 若涉及高度機密的共用資料夾,Snaplen 必須精確設為純 TCP 標頭長度(無 TCP Option 為 54 bytes,具 Timestamp 為 66 bytes)。此時重傳、Window Size、RST 與時序依然完整,足以排查「誰沒回應」。
  • custom.env 若需除錯 Full Payload(設為 0),執行前必須清楚認知,該封包檔將被寫入不可竄改的 WORM 儲存池,長達 180 天無法刪除。

2. 邊界保護:時間與檔案大小的雙重防線

  • timed 模式採用 tcpdump 的 -G/-W,
  • ring 模式採用 -C/-W,
  • 外層統一加上 timeout -s INT 提供最終牆上時間保護。

在實測中發現,-G/-W 的檔案切割(Rotation)必須在間隔時間過後的下一個進來封」才會被觸發。如果過濾條件完全沒有封包匹配,檔案將永遠不會 Rotate,因此外層的 MAX_SECONDS 強制逾時不可或缺。
此外,timeout 正常結束時會回傳離開碼 124,systemd 預設會將該服務視為 failed。在 Unit 補上 SuccessExitStatus=124,避免無封包進來的平靜時段在監控儀表板上誤報紅燈。

3. 介面自動解析(IFACE=auto)

不將網卡名稱寫死為 enP7s7。pcap-run.sh 透過 ip route get <PEER_IP> 動態提取路由出介面,未來無論機房更換網卡插槽或線路重編,設定檔均能自動適應。

第一次 pcap@nfs:journal 的開始與結束、done/ 裡的六個檔與 sidecar

在首次 pcap@nfs 執行中,共擷取 92,238 個封包,核心丟包數(Kernel dropped)為 0,順利切為 6 個標準封存檔與對應的 sha256 標記。


三、NAS 端的監控盲區破解

如果要在 NAS 側錄封包,這是很適合的沒錯,因為容量也剛好可側錄,但如果對 QNAP QuTS hero 6.0.2 進行盤點會發現:

  1. 指令列:完全沒有 tcpdump、tshark 或 dumpcap。
  2. App Center:現有 109 項套件中,唯有 ADRA NDR X 提到封包,但它是用來接收外來交換器鏡射的分析軟體,無法抓取 NAS 自身網卡流量。

![NAS 上找不到任何擷取工具]https://ithelp.ithome.com.tw/upload/images/20261007/20141816Kt9GzGK8yN.jpg

如果要完整錄製封包集中管理,第三方封包側錄軟體則有 SpesCap 這種商用封包擷取軟體可以用,具備多網卡獨立側錄、真實時戳與零暫存串流匯出的功能。

備案:利用接入交換器進行 Port Mirroring

既然端點無法側錄,或者是不用第三方軟體來集中處理封包側錄,那倒是可以把目光轉向節點與 NAS 之間的實體交換器,本場域採用的是 Mercury SE106 Pro。
透過瀏覽器登入管理介面,在「監控 → 端口監控」可以找到標準的 Port Mirroring 功能:

Mercury SE106 Pro 的端口監控頁面,六個埠目前都沒有被鏡射

實務上若需啟用交換器鏡射,必須預先考量以下三點:

  1. 混雜模式與權限:抓取鏡射流量的專用工作站必須開啟 Promiscuous 模式(需要 CAP_NET_ADMIN),並且能看見該網段所有流量,過濾條件必須寫得極度嚴謹。
  2. 頻寬瓶頸與丟包:SE106 Pro 為 2.5GbE 交換器。而在後續實測中,NFS 大檔讀取的單秒峰值流量高達 315 MB/s(約 2.52 Gb/s),雙向複製至 2.5G 監控埠必然引發溢流丟包(Buffer Drop)。若要進行微秒級的精確比對,監控埠必須接至 10G SFP+ 埠。
  3. 時序對齊方案:在未接入鏡射側錄器前,採取在 NVIDIA Spark 端擷取封包,搭配 NAS QuLog 系統日誌事件(已於 Day 21 接入 VictoriaLogs)進行時序對齊,依然能清晰定位問題斷點。

四、現場基線分析:NFS 與 Corosync

排錯的本質是知道異常與正常的差異,所以呀,得先在未發生故障時,先行收錄兩組服務的正常通訊當基準線 baseline。

1. NFS 流量基準線(nconnect=8 讀取模式)

在 spark01 上,NFS 掛載參數設定為 nconnect=8(建立 8 條並行 TCP 連線直連 2049 Port)。我們在離線工作站使用 tshark 分析 WORM 內的擷取檔:

# 查看 8 條 TCP 連線的各別流量負載
tshark -r nfs-20261007T003358Z.pcap -q -z conv,tcp

# 分析 NFSv4 操作型別與延遲
tshark -r nfs-20261007T003358Z.pcap -q -z rpc,programs

# 檢測 TCP 重傳(Retransmission)與零視窗(Zero Window)
tshark -r nfs-20261007T003358Z.pcap -Y 'tcp.analysis.retransmission || tcp.analysis.zero_window' | wc -l

# 每秒 I/O 與封包突波統計
tshark -r nfs-20261007T003358Z.pcap -q -z io,stat,1

基準線資料特徵:

  • 閒置期:50 分鐘的閒置期中,8 條連線僅維持微量的 TCP Keep-Alive(每小時約 213 對)與週期性 GETATTR 輪詢,每 10 分鐘僅約 200 個封包。
  • 載入期(14.25 秒傳輸 2.46 GB 模型):
    • 8 條 TCP 連線負載極度均衡,各分攤 307~308 MB。
    • RPC 呼叫共 2,419 次,平均回應延遲為 8.1 ms,最慢 0.72 秒。
    • TCP 重傳與零視窗為 0。
    • 峰值輸送量達到每秒 11,296 個封包、315 MB/s。

針對未來突發的 NFS 中斷,去設定 nfs-ring.env(30 檔各 100 MB,共 3 GB 環狀緩衝)。即便在持續大流量讀取下,依然能保留故障發生前至少 25 分鐘的關鍵底層封包,靜待下一次異常捕捉。

2. Corosync 叢集通訊基線

Corosync 是 Proxmox VE 雙節點高可用叢集的心臟。在 pve1 上側錄 10 分鐘流量:

# 觀察節點間 knet (UDP 5405) 每秒封包發送規律
tshark -r corosync-20261006T175324Z.pcap -q -z io,stat,1,'udp.port==5405'

# 觀察 QDevice (TCP 5403) TLS 心跳間隔
tshark -r corosync-20261006T175324Z.pcap -Y 'tcp.port==5403 && tcp.len>0' -T fields -e frame.time_delta_displayed | sort -n | tail -n 3

基準線資料特徵:

  • UDP 5405 (knet 流量):兩節點間雙向對稱發送,10 分鐘內共 107,709 個封包。呈現雙層節奏:底層為每秒 74 次的 Token 傳遞,每 5 秒出現一次約 300~500 pps 的叢集同步波峰,整體平均維持在 180 pps。
  • TCP 5403 (QDevice 仲裁心跳):節點與外部仲裁 VM 保持單一長連線,在正常狀態下絕無 TCP SYN,而是精準以 8.00 秒(誤差 $\le 5\text{ ms}$) 為週期發送 36-byte TLS 紀錄,回應延遲中位數僅 0.66 ms。過去在演練中看到的每 8 秒 SYN,確實是斷線後的重連跡象。

五、受限傳輸與 WORM 封存鏈結

封包擷取檔不同於一般日誌,它不上傳至 VictoriaLogs 熱層,而是直接進入冷層 WORM 儲存槽。

1. 嚴格限縮的拉取通道(rrsync Sandbox)

收集端透過 SSH 提取各節點的 done/ 目錄。我們在節點端 authorized_keys 施加強制命令限制:

command="rrsync -ro /var/lib/pcap/done",restrict ssh-ed25519 AAAAC3... pcap-pull

此設定將連線強制限縮在 rrsync 唯讀模式下,實測防護能力:

  • 嘗試寫入檔案 $\rightarrow$ 被拒絕
  • 嘗試路徑遍歷(../) $\rightarrow$ 被拒絕
  • 嘗試執行任意指令、請求 PTY、建立 Port 轉發 $\rightarrow$ 全數被 SSH 核心阻斷

2. 封裝清冊與端到端驗證

收集端的 pcap-pull.sh 自動解析封包檔,產出 MANIFEST.tsv,欄位包含:
file, bytes, packets, first_ts, last_ts, truncated, sha256。
其中 truncated=1 標記可識別因進程強制終止而中斷的殘缺封包。

第一批進 WORM:MANIFEST.tsv、verify-pcap.sh --against-index 的輸出與鎖定後的 rm

3. WORM 鎖定延遲

在進行不可竄改性驗證時,這個資訊環境場域剛好碰到技術陷阱:

10 分鐘鎖定延遲(Locking Grace Period)

我們的 WORM 資料夾具備「檔案寫入後 10 分鐘才真正鎖定」的緩衝期。若在 10 分鐘內對檔案進行修改,倒數計時器會重置。
在初次測試時,工程師在檔案寫入 2 分鐘後立即執行 rm 與檔案追加測試,結果成功刪除與修改。

  • 修復與防護:排程設定原先為每小時 :40 拉取、:50 驗證,兩者相差 10 分鐘剛好落在延遲邊緣。我們將排程調整為 :55 執行驗證,確保驗證執行時檔案已跨過鎖定門檻。
  • 寫入後滿 12 分鐘再度測試,無論是一般使用者還是 root,執行 rm -rf 皆全數回傳 Operation not permitted。

六、部署流程與操作指引

1. 收集端配置(以 metrics 身份執行)

# 產生專用金鑰與本機目錄
sudo -u metrics ssh-keygen -t ed25519 -N '' -f /var/lib/metrics/.ssh/id_pcap -C pcap-pull
sudo install -d -o metrics -g metrics /var/lib/onprem-pcap
sudo -u metrics mkdir -p /mnt/worm/pcap

# 註冊 Cron 定時任務(每小時 :40 拉取,:55 執行嚴格驗證)
cat pcap/collector.cron | sudo tee -a /etc/cron.d/onprem-logs

2. 節點端安裝(DGX Spark 與 PVE)

# 安裝服務與匯入公鑰沙盒限制
sudo ./pcap/install-pcap.sh --pull-key collector-id_pcap.pub

# (可選)將值班帳號加入授權組
sudo usermod -aG pcap-ops duty-engineer

# 啟動對應監控服務
sudo systemctl start pcap@nfs        # Spark 節點
sudo systemctl start pcap@corosync   # PVE 叢集節點

3. 收集端手動拉取與驗證

# 手動執行拉取測試
sudo -u metrics env PCAP_NODES="spark01=pcap@192.168.2.131:/" SSH_KEY=/var/lib/metrics/.ssh/id_pcap ./pcap/pcap-pull.sh

# 執行冷層索引比對驗證
sudo -u metrics ./pcap/verify-pcap.sh --latest --against-index

七、維運防線原則

在本篇架構落地後,我們建立了幾條不可妥協的防線原則:

  1. 禁止長期掛載大規模 Ring Capture:封包檔佔用高昂的儲存空間與隱私成本,除錯完畢務必隨手停止服務。
  2. 禁止迷信「小 Snaplen 即安全」:如前文實證,Snaplen 256 依然會洩漏大量的 Payload 碎片。若有高度去識別化要求,直接截斷至純 TCP 標頭(54 bytes)。
  3. 禁止濫用 tcpdump -i any:any 介面採用 Linux Cooked Header,會遺失實體 Ethernet 標頭,在雙 GPU 伺服器上,更會將 CX-7 網卡動輒數十 GB 的 NCCL 巨量流量無差別捲入,瞬間塞爆磁碟。

明日預告

明天我們將回到邊界防火牆,將視角從「阻擋外來攻擊」轉向「內部人員對外行為審查」。我們將同時串接即時線上檢索管道,並使用基於純瀏覽器端的離線分析工具 fortigate-log-viewer 讀匯出檔,同一份檔案兩邊各跑一次,答案要一樣。


系列文章與程式碼索引:onprem-ops-30days

本日程式碼:onprem-logs

參考資料

  • tcpdump(1) -G、-W、-C、-p、-s、-Z 的定義,-W 與 -G 合用時到達檔數即結束
  • pcap-savefile(5) 24 位元組檔頭與 16 位元組每筆紀錄標頭的格式,pcap-stat.py 依此讀檔
  • capabilities(7) CAP_NET_RAW 開 raw 與 packet socket,CAP_NET_ADMIN 設定介面(含混雜模式),ambient 能力在 execve 後保留
  • systemd.exec(5) AmbientCapabilities=、CapabilityBoundingSet=、RestrictAddressFamilies=、ReadWritePaths=、RuntimeMaxSec=、LimitFSIZE=
  • Ubuntu 24.04 tcpdump 4.99.4-3ubuntu4.24.04.1 與 Debian 13 tcpdump 4.99.5-2 的 /etc/apparmor.d/usr.bin.tcpdump,/**.pcap 與 /**.pcap[0-9]* 的規則(文中引用原文),與 LP: #2052493 20.04 與 22.04 缺少加數字的規則
  • rrsync(1) 把一把 ssh 金鑰限制在一個目錄的唯讀 rsync
  • sshd(8) AUTHORIZED_KEYS FILE FORMAT command=、restrict,forced command 經使用者的 shell 執行
  • sudoers(5) 萬用字元在命令列參數中的比對方式
  • tmpfiles.d(5) d 類型的 age 欄位
  • Proxmox VE 文件 Cluster Manager corosync 的 UDP 5405 到 5412 與 QDevice 的 TCP 5403
  • SpesCap SpesCap 全封包側錄軟體
  • tshark(1) -z conv,tcp、-z rpc,programs、-z io,stat

上一篇
Day 22|被擋下的封包才是便宜情報:從防火牆靜默盲點到日誌降噪與自動化告警
下一篇
Day 24|稽核問「某台工作站上個月連到哪裡了」?FortiGate AUP 雙軌日誌實踐:線上 LogsQL 檢索與離線對帳修煉
系列文
地端機房的三十天維運:開源服務封裝、GPU 節點守護與可稽核的變更管理 共 24 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言